x86: Clean up APIC local timer handling.
authorKeir Fraser <keir.fraser@citrix.com>
Tue, 3 Nov 2009 09:33:22 +0000 (09:33 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Tue, 3 Nov 2009 09:33:22 +0000 (09:33 +0000)
1. Writing TMICT=0 disables the timer. Use this fact to simplify and
improve reprogram_timer(). In particular, we always write TMICT, and
write zero when we do not need a timer interrupt.

2. In HPET broadcast timer handler, set TMICT=0 when we mask the APIC
local timer. May as well do this early, before entering deep sleep.

3. In HVM-guest APIC emulation, disable the emulated local timer when
the guest sets TMICT=0. Previously we would issue an immediate
one-shot interrupt.

Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/arch/x86/apic.c
xen/arch/x86/hpet.c
xen/arch/x86/hvm/vlapic.c

index b9004413a89330a780ebee9c7699e3e00f275b6d..64be332ca6ceb0fafabac3dc6c359cfe5be2aff5 100644 (file)
@@ -1178,65 +1178,25 @@ void enable_APIC_timer(void)
 #undef APIC_DIVISOR
 
 /*
- * reprogram the APIC timer. Timeoutvalue is in ns from start of boot
- * returns 1 on success
- * returns 0 if the timeout value is too small or in the past.
+ * reprogram_timer: Reprogram the APIC timer.
+ * Timeout is a Xen system time (nanoseconds since boot); 0 disables the timer.
+ * Returns 1 on success; 0 if the timeout is too soon or is in the past.
  */
 int reprogram_timer(s_time_t timeout)
 {
-    s_time_t    now;
-    s_time_t    expire;
-    u64         apic_tmict;
+    s_time_t expire;
+    u32 apic_tmict = 0;
 
-    /*
-     * If we don't have local APIC then we just poll the timer list off the
-     * PIT interrupt.
-     */
+    /* No local APIC: timer list is polled via the PIT interrupt. */
     if ( !cpu_has_apic )
         return 1;
 
-    /*
-     * We use this value because we don't trust zero (we think it may just
-     * cause an immediate interrupt). At least this is guaranteed to hold it
-     * off for ages (esp. since the clock ticks on bus clock, not cpu clock!).
-     */
-    if ( timeout == 0 )
-    {
-        apic_tmict = 0xffffffff;
-        goto reprogram;
-    }
-
-    now = NOW();
-    expire = timeout - now; /* value from now */
-
-    if ( expire <= 0 )
-    {
-        Dprintk("APICT[%02d] Timeout in the past 0x%08X%08X > 0x%08X%08X\n", 
-                smp_processor_id(), (u32)(now>>32), 
-                (u32)now, (u32)(timeout>>32),(u32)timeout);
-        return 0;
-    }
-
-    /* conversion to bus units */
-    apic_tmict = (((u64)bus_scale) * expire)>>18;
-
-    if ( apic_tmict >= 0xffffffff )
-    {
-        Dprintk("APICT[%02d] Timeout value too large\n", smp_processor_id());
-        apic_tmict = 0xffffffff;
-    }
-
-    if ( apic_tmict == 0 )
-    {
-        Dprintk("APICT[%02d] timeout value too small\n", smp_processor_id());
-        return 0;
-    }
+    if ( timeout && ((expire = timeout - NOW()) > 0) )
+        apic_tmict = min_t(u64, (bus_scale * expire) >> 18, UINT_MAX);
 
- reprogram:
-    /* Program the timer. */
     apic_write(APIC_TMICT, (unsigned long)apic_tmict);
 
-    return 1;
+    return apic_tmict || !timeout;
 }
 
 fastcall void smp_apic_timer_interrupt(struct cpu_user_regs * regs)
index 380066262401e996921f05a11a5e78f8644e0987..c775aaa4869aebdfb8d517a3a89f21e901cec418 100644 (file)
@@ -642,6 +642,8 @@ void hpet_broadcast_enter(void)
     if ( hpet_attach_channel )
         hpet_attach_channel(cpu, ch);
 
+    /* Cancel any outstanding LAPIC timer event and disable interrupts. */
+    reprogram_timer(0);
     disable_APIC_timer();
 
     cpu_set(cpu, ch->cpumask);
@@ -664,11 +666,8 @@ void hpet_broadcast_exit(void)
 
     if ( cpu_test_and_clear(cpu, ch->cpumask) )
     {
-        /* Cancel any outstanding LAPIC event and re-enable interrupts. */
-        reprogram_timer(0);
-        enable_APIC_timer();
-        
         /* Reprogram the deadline; trigger timer work now if it has passed. */
+        enable_APIC_timer();
         if ( !reprogram_timer(per_cpu(timer_deadline, cpu)) )
             raise_softirq(TIMER_SOFTIRQ);
 
index 6d1d7b9e71783cb82305332fbb275be1381252f3..83057f91f552d67590b29813d9ce6a7cf8a7847e 100644 (file)
@@ -667,10 +667,17 @@ static int vlapic_write(struct vcpu *v, unsigned long address,
 
     case APIC_TMICT:
     {
-        uint64_t period = (uint64_t)APIC_BUS_CYCLE_NS *
-                            (uint32_t)val * vlapic->hw.timer_divisor;
+        uint64_t period;
 
         vlapic_set_reg(vlapic, APIC_TMICT, val);
+        if ( val == 0 )
+        {
+            destroy_periodic_time(&vlapic->pt);
+            break;
+        }
+
+        period = ((uint64_t)APIC_BUS_CYCLE_NS *
+                  (uint32_t)val * vlapic->hw.timer_divisor);
         create_periodic_time(current, &vlapic->pt, period, 
                              vlapic_lvtt_period(vlapic) ? period : 0,
                              vlapic->pt.irq, vlapic_pt_cb,